
#include "socket5.h"
#include <stdlib.h>
#include <string.h>

extern event_base * g_base;

client* client_new()
{
	client* client = (client*)malloc(sizeof(struct client));
	client->socket = -1;
	client->remote_socket = -1;
	client->read_event = NULL;
	client->timeout_event = NULL;
    client->remote_read_event = NULL;
    client->remote_timeout_event = NULL;
}

void client_release(client *user)
{
	if (user->read_event != NULL)
	{
		event_del(user->read_event);
	}
	if (user->timeout_event != NULL)
	{
		event_del(user->timeout_event);		
	}
    if (user->remote_read_event != NULL)
    {
        event_del(user->remote_read_event);
    }
    if (user->remote_timeout_event != NULL)
    {
        event_del(user->remote_timeout_event);
    }
	if (user->socket != -1) 
	{
		evutil_closesocket(user->socket);		
	}

	if (user->remote_socket)
	{
		evutil_closesocket(user->remote_socket);
	}
	free(user);
}


void client_select_method(evutil_socket_t socket, short what, void*arg)
{
	char buffer[BUFFER_SIZE] = { 0 };
	char reply_buffer[2] = { 0 };
	client* user = (client*)arg;	
	int ret = recv(socket, buffer, BUFFER_SIZE, 0);
	if (ret <= 0)
	{
		client_release(user);
		perror("receive data error:");
		return;
	}
	METHOD_SELECT_REQUEST *request;
	METHOD_SELECT_RESPONSE * response;

	request = (METHOD_SELECT_REQUEST*)buffer;
	response = (METHOD_SELECT_RESPONSE*)reply_buffer;
	response->version = VERSION;
	METHOD_SELECT_REQUEST *request;
	METHOD_SELECT_RESPONSE * response;
	client* user = (client*)arg;

	request = (METHOD_SELECT_REQUEST*)buffer;
	response = (METHOD_SELECT_RESPONSE*)reply_buffer;
	response->version = VERSION;
	if (ret<=2 || request->version != VERSION)
	{
		response->select_method = METHOD_REJECT;
		send(socket, response, sizeof(response), 0);
		client_release(user);
		return;
	}
	unsigned int num = request->number_methods;
	event * auth_event;
	//万一客户端发送的数据 没有按照协议来 怎么搞
	//不用循环，只取第一个字节的认证方法
	if (METHOD_ALLOW_NONE && request->methods[0] == METHOD_NONE)
	{
		response->select_method = METHOD_NONE;
		auth_event = event_new(g_base, socket, EV_READ, parse_commond, (void*)user);
	}
	else if (request->methods[0] == METHOD_GSSAPI)
	{
		response->select_method = METHOD_GSSAPI;
		auth_event = event_new(g_base, socket, EV_READ, client_auth_wssapi, (void*)user);
	}
	else{
		response->select_method = METHOD_USERNAME_PASSWORD;
        auth_event = event_new(g_base, socket, EV_READ, client_auth_name_pwd, (void*)user);
	}
	event_add(auth_event, NULL);
	event_del(user->read_event);
	user->read_event = auth_event;
	//更新超时事件
	timeval time;
	time.tv_sec = 60;
	time.tv_usec = 0;
	event_add(user->timeout_event, &time);

}

void parse_commond(evutil_socket_t socket, short what, void* arg)
{
	client* user = (client* )arg;
	SOCKET5_REQUEST * request;
	SOCKET5_RESPONSE * response;
	char recv_buffer[BUFFER_SIZE];
	int ret = recv(socket, recv_buffer, BUFFER_SIZE, 0);
	//报错，或者客户端close掉
	if (ret<=0)
	{
		client_release(user);
		return ;
	}

	request = (SOCKET5_REQUEST*)recv_buffer;
    response->version = VERSION_SOCKET5;
	if (request->version != VERSION)
    {
        response->reply = 0x05;
        response->reserved = 0x00;
        send(socket, response,3, 0);
        client_release(user);
        return ;
    }
    struct sockaddr_in sin;
    memset(&sin, 0 , sizeof(sin));
    if (request->address_type == ATYP_IPV4)
    {
        memcpy(&sin.sin_addr.s_addr, &request->address_type+sizeof(request->address_type), 4);
        memcpy(&sin.sin_addr.s_addr, &request->address_type+sizeof(request->address_type)+4, 2);
    } else if (request->address_type == ATYP_DOMAIN)
    {
        char domain_length = *(&request->address_type + sizeof(request->address_type));
        char domain[255] = {0};
        strncpy(domain, &request->address_type+2, (unsigned int)domain_length);
        struct hostent * host = gethostbyname(domain);
        if (host == NULL)
        {
            client_release(user);
            return ;
        }
        memcpy(&sin.sin_addr, host->h_addr_list[0], host->h_length);
		memcpy(&sin.sin_port, &request->address_type + sizeof(request->address_type) +
			sizeof(domain_length) + domain_length, 2);
    } else {
        response->reply = 0x07;
        response->reserved = 0x00;
        send(socket, response, 3, 0);
        client_release(user);
        return ;
    }

    evutil_socket_t remote_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (remote_socket<=0)
    {
        response->reply = 0x01;
        response->reserved = 0x00;
        send(socket, response, sizeof(response), 0);
        client_release(user);
        return ;
    }

    ret = connect(remote_socket, (struct sockaddr*)&sin, sizeof(sin));
    if (ret == -1)
    {
        response->reply = 0x05;
        response->reserved = 0x00;
        send(socket, response, 3, 0);
        client_release(user);
        return ;
    }

    event* remote_read_event = event_new(g_base, EV_READ, remote_read_event_callback, (void*)user);
    event_add(remote_read_event, NULL);
    struct timeval time;
    time.tv_sec = 60;
    time.tv_usec = 0;
    user->remote_read_event = remote_read_event;
    event* remote_timeout_event = event_new(g_base, EV_TIMEOUT, remote_timeout_event_callback, (void*)user);
    user->remote_timeout_event = remote_timeout_event;
    event_add(remote_timeout_event, &time);
    response->reply = 0x00;
    response->reserved = 0x01;
    response->address_type = 0x01;
    send(socket, response, sizeof(response), 0);
    struct timeval time;
    time.tv_sec = 60;
    time.tv_usec = 0;
    event_add(user->timeout_event, NULL);//更新超时事件
    return ;
	
}

void remote_read_event_callback(evutil_socket_t socket, short what, void * arg)
{
    client* user = (client*)arg;
    char recv_buffer[BUFFER_SIZE] = {0};
    int ret = recv(socket, recv_buffer, BUFFER_SIZE, 0);
    if (ret <=0)
    {
        //怎么立即触发超时事件 
        return ;
    }
    send(user->socket, recv_buffer, ret, 0);
    event_add(user->remote_read_event, NULL);
    return ;
}

void remote_timeout_event_callback(evutil_socket_t socket, short what, void* arg)
{
    client* user = (client*)arg;
    char recv_buffer[BUFFER_SIZE] = {0};
    int ret = recv(socket, recv_buffer, BUFFER_SIZE, 0);
    if (ret <=0)
    {
        return ;
    }
    send(user->remote_socket, recv_buffer, ret, 0);
    event_add(user->read_event);
    return ;
}

void client_auth_name_pwd(evutil_socket_t socket, short what, void* arg)
{
    client* user = (client*)arg;
    char recv_buffer[BUFFER_SIZE] = {0};
    AUTH_REQUEST * request;
    AUTH_RESPONSE * response;

    int ret = recv(socket, recv_buffer, BUFFER_SIZE, 0);
    if (ret<=0)
    {
        client_release(user);
        return;
    }
    request = (AUTH_REQUEST*)recv_buffer;
    //版本号，用户名长度，用户名，密码长度，密码 至少5字节
    if (ret<5)
    {
        response->version = 0x05;
        response->result = 0x01;
        send(socket, response, 2, 0);
        client_release(user);
        return;
    }

    int name_len = request->name_len;

    if ((name_len+1)>=ret) {
        response->version = 0x05;
        response->result = 0x01;
        send(socket, response, 2, 0);
        client_release(user);
        return;
    }

    char user_name[255] = {0};

    memcpy(user_name, recv_buffer[1], name_len);
    char pwd_str[2] = {0};
    memcpy(pwd_str, recv_buffer[1+name_len], 1);
    int pwd_len = (int)pwd_str[0];

    if (pwd_len+name_len+3 > ret)
    {
        response->version = 0x05;
        response->result = 0x01;
        send(socket, response, 2, 0);
        client_release(user);
        return ;
    }

    int flag_name = strncmp(user_name, AUTH_NAME_DEFAULT,strlen(AUTH_NAME_DEFAULT));
    if (flag_name != 0)
    {
        response->version = 0x05;
        response->result = 0x02;
        send(socket, response, 2, 0);
        client_release(user);
        return ;
    }
    if (strncmp(recv_buffer[3+name_len], AUTH_PWD_DEFAULT, strlen(AUTH_PWD_DEFAULT)) !=0)
    {
        response->version = 0x05;
        response->result = 0x03;
        send(socket, response, 2, 0);
        client_release(user);
    }

    response->version =0x05;
    response->result = 0x00;
    send(socket, response, 2, 0);
    //更新事件，和添加事件
    event* parse_commond_event = event_new(socket, EV_READ, parse_commond, (void*)user);
    event_add(parse_commond_event, NULL);
    user->read_event = parse_commond_event;
    timeval time;
    time.tv_sec = 60;
    time.tv_usec = 0;
    event_add(user->timeout_event, &time);
    return;
}
